1. Regressão Logística com Regularização

Nesta parte do trabalho, será implementada a Regressão Logística Regularizada para prever se os microchips de uma usina de fabricação passam na garantia de qualidade (QA). Durante a QA, cada microchip passa por vários testes para garantir se está funcionando corretamente. Dessa forma, a Gestão de Produto da fábrica terá o resultados de teste para alguns microchips em dois testes diferentes.

A partir desses dois testes, será determinado se os microchips deveriam ser aceitos ou rejeitados. Para auxiliar a tomar a decisão, há um conjunto de dados com resultados de testes anteriores sobre microchips, a partir do qual é possível construir um modelo de Regressão Logística.

O arquivo {ex2data2.txt} contém os dados a serem usados nessa parte do trabalho. A primeira coluna corresponde aos resultados do primeiro teste, enquanto que a segunda coluna corresponde aos resultados do segundo teste. A terceira coluna contém os valores da classe (y = 0 significa rejeitado no teste, e y = 1 significa aceito no teste).

1.1 Visualização dos Dados

Para a maioria dos conjuntos de dados do mundo real, não é possível criar um gráfico para visualizar seus pontos. Mas, para o conjunto de dados fornecido, isso é possível. Implemente um script em Python que produza um gráfico de dispersão (scatter plot) dos dados fornecidos. Após finalizado, seu script deve produzir um resultado similar ao apresentado na Figura abaixo.


In [39]:
#import os 
import pandas as pd
import numpy as np
import matplotlib as plt

from numpy import loadtxt, where, append, zeros, ones, array, linspace, logspace
from pylab import scatter, show, legend, xlabel, ylabel

#%matplotlib inline

# Carregando o arquivo gerado pelo MATLAB
#import scipy.io
#mat = scipy.io.loadmat('file.mat')

In [40]:
# Construindo um dataset com base num Dataframe, já identificando colunas e exibindo seus primeiros 20 registros.
df = pd.read_csv('am-T2-dados/ex2data2.txt', names=['QATest1', 'QATest2', 'QAcceptance'])
df.head()


Out[40]:
QATest1 QATest2 QAcceptance
0 0.051267 0.69956 1
1 -0.092742 0.68494 1
2 -0.213710 0.69225 1
3 -0.375000 0.50219 1
4 -0.513250 0.46564 1

In [41]:
# Visualização da Distribuição dos Dados conforme Histograma

df.hist()
plt.pyplot.show()

df.QATest1.hist(), df.QATest2.hist(), df.QAcceptance.hist()
plt.pyplot.show()



In [42]:
df.describe()


Out[42]:
QATest1 QATest2 QAcceptance
count 118.000000 118.000000 118.000000
mean 0.054779 0.183102 0.491525
std 0.496654 0.519743 0.502060
min -0.830070 -0.769740 0.000000
25% -0.372120 -0.254385 0.000000
50% -0.006336 0.213455 0.000000
75% 0.478970 0.646562 1.000000
max 1.070900 1.108900 1.000000

In [43]:
df.QATest1.describe().round(2)


Out[43]:
count    118.00
mean       0.05
std        0.50
min       -0.83
25%       -0.37
50%       -0.01
75%        0.48
max        1.07
Name: QATest1, dtype: float64

In [44]:
df.QATest2.describe().round(2)


Out[44]:
count    118.00
mean       0.18
std        0.52
min       -0.77
25%       -0.25
50%        0.21
75%        0.65
max        1.11
Name: QATest2, dtype: float64

In [45]:
df.QAcceptance.describe().round(2)


Out[45]:
count    118.00
mean       0.49
std        0.50
min        0.00
25%        0.00
50%        0.00
75%        1.00
max        1.00
Name: QAcceptance, dtype: float64

In [46]:
#df.drop(labels='QATest1', axis=1)
#df.drop(labels='QATest2', axis=1)
#df.drop(labels='QAcceptance', axis=1)


#load the dataset
data = np.loadtxt('am-T2-dados/ex2data2.txt', delimiter=',')

In [ ]:


In [48]:
X = data[:, 0:2]
y = data[:, 2]

pos = where(y == 1)
neg = where(y == 0)
scatter(X[pos, 0], X[pos, 1], marker='+', c='xkcd:black', label='Not Admitted')
scatter(X[neg, 0], X[neg, 1], marker='o', c='xkcd:yellow', label='Admitted')
legend(['Admitted', 'Not Admitted'])
xlabel('Exam 1 score')
ylabel('Exam 2 score')
show()


1.2 Mapeamento de características (feature mapping)

Uma maneira de tornar os dados mais apropriados para a classificação é criar mais características a partir das já existentes. Para isso, você deve criar uma função mapFeature. Essa função deve ser implementada em um arquivo de nome mapFeature.py, que irá mapear as características para todos os termos polinomiais de x1 e x2, até a sexta potência. Como resultado desse mapeamento, nosso vetor de duas características (os escores nos dois testes de QA) será transformado em um vetor de 28 dimensões.

Um classificador que usa regressão logística treinado nesse vetor de características de maior dimensão terá uma fronteira de decisão mais complexa e parecerá não-linear quando desenhado em um gráfico bidimensional.

Embora o mapeamento de características nos permita construir um classificador mais expressivo, também é mais suscetível a sobreajuste (overfitting). Desse modo, será implementada a Regressão Logística Regularizada sobre os dados fornecidos e também verá como a regularização pode ajudar a combater o problema do sobreajuste.


In [ ]:


In [49]:
def map_feature(X1, X2):
    '''
    Função que mapeia características p/ os termos polinomiais X1 e X2 até a 6ª potência.
    Retorna um novo conjunto com mais características, através do algoritmo de mapping
    X1, X2, X1 ** 2, X2 ** 2, X1*X2, X1*X2 ** 2, etc...
    Os parâmetros X1, X2 devem ser do mesmo tamanho
    '''
    # Potência padrão para o mapeamento
    potencia = 6
    
    X1.shape = (X1.size, 1)
    X2.shape = (X2.size, 1)
    
    features = np.ones(shape=(X1.size, 1))

    for i in range(1, potencia + 1):
        for j in range(i + 1):
            r = (X1 ** (i - j)) * (X2 ** j)
            features = append(features, r, axis=1)

    return features

In [50]:
X = data[:, 0:2]
y = data[:, 2]

pos = where(y == 1)
neg = where(y == 0)
scatter(X[pos, 0], X[pos, 1], marker='o', c='b')
scatter(X[neg, 0], X[neg, 1], marker='x', c='r')
xlabel('Microchip Test 1')
ylabel('Microchip Test 2')
legend(['y = 1', 'y = 0'])


m, n = X.shape
y.shape = (m, 1)

it = map_feature(X[:, 0], X[:, 1])

In [51]:
it.shape


Out[51]:
(118, 28)

In [52]:
print(pd.DataFrame(it))


      0         1         2         3         4         5             6   \
0    1.0  0.051267  0.699560  0.002628  0.035864  0.489384  1.347453e-04   
1    1.0 -0.092742  0.684940  0.008601 -0.063523  0.469143 -7.976812e-04   
2    1.0 -0.213710  0.692250  0.045672 -0.147941  0.479210 -9.760555e-03   
3    1.0 -0.375000  0.502190  0.140625 -0.188321  0.252195 -5.273438e-02   
4    1.0 -0.513250  0.465640  0.263426 -0.238990  0.216821 -1.352032e-01   
5    1.0 -0.524770  0.209800  0.275384 -0.110097  0.044016 -1.445130e-01   
6    1.0 -0.398040  0.034357  0.158436 -0.013675  0.001180 -6.306380e-02   
7    1.0 -0.305880 -0.192250  0.093563  0.058805  0.036960 -2.861892e-02   
8    1.0  0.016705 -0.404240  0.000279 -0.006753  0.163410  4.661648e-06   
9    1.0  0.131910 -0.513890  0.017400 -0.067787  0.264083  2.295267e-03   
10   1.0  0.385370 -0.565060  0.148510 -0.217757  0.319293  5.723131e-02   
11   1.0  0.529380 -0.521200  0.280243 -0.275913  0.271649  1.483551e-01   
12   1.0  0.638820 -0.243420  0.408091 -0.155502  0.059253  2.606967e-01   
13   1.0  0.736750 -0.184940  0.542801 -0.136255  0.034203  3.999083e-01   
14   1.0  0.546660  0.487570  0.298837  0.266535  0.237725  1.633623e-01   
15   1.0  0.322000  0.582600  0.103684  0.187597  0.339423  3.338625e-02   
16   1.0  0.166470  0.538740  0.027712  0.089684  0.290241  4.613260e-03   
17   1.0 -0.046659  0.816520  0.002177 -0.038098  0.666705 -1.015795e-04   
18   1.0 -0.173390  0.699560  0.030064 -0.121297  0.489384 -5.212813e-03   
19   1.0 -0.478690  0.633770  0.229144 -0.303379  0.401664 -1.096890e-01   
20   1.0 -0.605410  0.597220  0.366521 -0.361563  0.356672 -2.218956e-01   
21   1.0 -0.628460  0.334060  0.394962 -0.209943  0.111596 -2.482178e-01   
22   1.0 -0.593890  0.005117  0.352705 -0.003039  0.000026 -2.094682e-01   
23   1.0 -0.421080 -0.272660  0.177308  0.114812  0.074343 -7.466101e-02   
24   1.0 -0.115780 -0.396930  0.013405  0.045957  0.157553 -1.552032e-03   
25   1.0  0.201040 -0.601610  0.040417 -0.120948  0.361935  8.125450e-03   
26   1.0  0.466010 -0.535820  0.217165 -0.249697  0.287103  1.012012e-01   
27   1.0  0.673390 -0.535820  0.453454 -0.360816  0.287103  3.053515e-01   
28   1.0 -0.138820  0.546050  0.019271 -0.075803  0.298171 -2.675199e-03   
29   1.0 -0.294350  0.779970  0.086642 -0.229584  0.608353 -2.550305e-02   
..   ...       ...       ...       ...       ...       ...           ...   
88   1.0 -0.403800  0.706870  0.163054 -0.285434  0.499665 -6.584138e-02   
89   1.0 -0.380760  0.918860  0.144978 -0.349865  0.844304 -5.520189e-02   
90   1.0 -0.507490  0.904240  0.257546 -0.458893  0.817650 -1.307021e-01   
91   1.0 -0.547810  0.706870  0.300096 -0.387230  0.499665 -1.643955e-01   
92   1.0  0.103110  0.779970  0.010632  0.080423  0.608353  1.096232e-03   
93   1.0  0.057028  0.918860  0.003252  0.052401  0.844304  1.854661e-04   
94   1.0 -0.104260  0.991960  0.010870 -0.103422  0.983985 -1.133322e-03   
95   1.0 -0.081221  1.108900  0.006597 -0.090066  1.229659 -5.358028e-04   
96   1.0  0.287440  1.087000  0.082622  0.312447  1.181569  2.374880e-02   
97   1.0  0.396890  0.823830  0.157522  0.326970  0.678696  6.251878e-02   
98   1.0  0.638820  0.889620  0.408091  0.568307  0.791424  2.606967e-01   
99   1.0  0.823160  0.663010  0.677592  0.545763  0.439582  5.577669e-01   
100  1.0  0.673390  0.641080  0.453454  0.431697  0.410984  3.053515e-01   
101  1.0  1.070900  0.100150  1.146827  0.107251  0.010030  1.228137e+00   
102  1.0 -0.046659 -0.579680  0.002177  0.027047  0.336029 -1.015795e-04   
103  1.0 -0.236750 -0.638160  0.056051  0.151084  0.407248 -1.326997e-02   
104  1.0 -0.150350 -0.367690  0.022605  0.055282  0.135196 -3.398680e-03   
105  1.0 -0.490210 -0.301900  0.240306  0.147994  0.091144 -1.178003e-01   
106  1.0 -0.467170 -0.133770  0.218248  0.062493  0.017894 -1.019588e-01   
107  1.0 -0.288590 -0.060673  0.083284  0.017510  0.003681 -2.403498e-02   
108  1.0 -0.611180 -0.067982  0.373541  0.041549  0.004622 -2.283008e-01   
109  1.0 -0.663020 -0.214180  0.439596  0.142006  0.045873 -2.914606e-01   
110  1.0 -0.599650 -0.418860  0.359580  0.251169  0.175444 -2.156222e-01   
111  1.0 -0.726380 -0.082602  0.527628  0.060000  0.006823 -3.832584e-01   
112  1.0 -0.830070  0.312130  0.689016 -0.259090  0.097425 -5.719317e-01   
113  1.0 -0.720620  0.538740  0.519293 -0.388227  0.290241 -3.742131e-01   
114  1.0 -0.593890  0.494880  0.352705 -0.293904  0.244906 -2.094682e-01   
115  1.0 -0.484450  0.999270  0.234692 -0.484096  0.998541 -1.136964e-01   
116  1.0 -0.006336  0.999270  0.000040 -0.006332  0.998541 -2.544062e-07   
117  1.0  0.632650 -0.030612  0.400246 -0.019367  0.000937  2.532156e-01   

           7         8             9       ...                 18  \
0    0.001839  0.025089  3.423536e-01      ...       8.998098e-04   
1    0.005891 -0.043509  3.213347e-01      ...       2.763825e-03   
2    0.031616 -0.102412  3.317332e-01      ...       1.515091e-02   
3    0.070620 -0.094573  1.266497e-01      ...       1.781011e-02   
4    0.122661 -0.111283  1.009603e-01      ...       2.659554e-02   
5    0.057775 -0.023098  9.234565e-03      ...       2.543047e-03   
6    0.005443 -0.000470  4.055512e-05      ...       6.425385e-06   
7   -0.017987 -0.011305 -7.105572e-03      ...      -6.648156e-04   
8   -0.000113  0.002730 -6.605685e-02      ...      -1.843363e-05   
9   -0.008942  0.034835 -1.357096e-01      ...      -2.361380e-03   
10  -0.083917  0.123046 -1.804196e-01      ...      -2.679412e-02   
11  -0.146063  0.143806 -1.415837e-01      ...      -3.967786e-02   
12  -0.099338  0.037852 -1.442344e-02      ...      -5.886075e-03   
13  -0.100386  0.025199 -6.325466e-03      ...      -3.433467e-03   
14   0.145704  0.129954  1.159073e-01      ...       3.463742e-02   
15   0.060406  0.109294  1.977477e-01      ...       2.050327e-02   
16   0.014930  0.048316  1.563643e-01      ...       4.333209e-03   
17   0.001778 -0.031108  5.443779e-01      ...       1.185145e-03   
18   0.021032 -0.084854  3.423536e-01      ...       1.029255e-02   
19   0.145225 -0.192273  2.545629e-01      ...       5.833158e-02   
20   0.218894 -0.215933  2.130115e-01      ...       7.807324e-02   
21   0.131941 -0.070134  3.727979e-02      ...       1.472410e-02   
22   0.001805 -0.000016  1.339819e-07      ...       4.725614e-08   
23  -0.048345 -0.031305 -2.027049e-02      ...      -3.594128e-03   
24  -0.005321 -0.018242 -6.253768e-02      ...      -8.383181e-04   
25  -0.024315  0.072763 -2.177435e-01      ...      -8.800556e-03   
26  -0.116362  0.133793 -1.538356e-01      ...      -3.340775e-02   
27  -0.242970  0.193332 -1.538356e-01      ...      -6.975737e-02   
28   0.010523 -0.041392  1.628161e-01      ...       3.137627e-03   
29   0.067578 -0.179069  4.744972e-01      ...       4.111135e-02   
..        ...       ...           ...      ...                ...   
88   0.115258 -0.201765  3.531983e-01      ...       5.759056e-02   
89   0.133215 -0.321477  7.757969e-01      ...       1.124736e-01   
90   0.232883 -0.414949  7.393518e-01      ...       1.904172e-01   
91   0.212129 -0.273722  3.531983e-01      ...       1.059933e-01   
92   0.008292  0.062727  4.744972e-01      ...       5.044699e-03   
93   0.002988  0.048149  7.757969e-01      ...       2.523041e-03   
94   0.010783 -0.102590  9.760734e-01      ...       1.061006e-02   
95   0.007315 -0.099874  1.363569e+00      ...       8.995262e-03   
96   0.089810  0.339630  1.284366e+00      ...       1.061165e-01   
97   0.129771  0.269368  5.591300e-01      ...       8.807510e-02   
98   0.363046  0.505577  7.040664e-01      ...       2.873232e-01   
99   0.449251  0.361847  2.914474e-01      ...       1.974826e-01   
100  0.290700  0.276752  2.634733e-01      ...       1.194731e-01   
101  0.114855  0.010741  1.004507e-03      ...       1.151995e-03   
102 -0.001262 -0.015679 -1.947892e-01      ...      -4.240683e-04   
103 -0.035769 -0.096416 -2.598895e-01      ...      -1.456695e-02   
104 -0.008312 -0.020327 -4.971019e-02      ...      -1.123705e-03   
105 -0.072548 -0.044680 -2.751626e-02      ...      -6.612317e-03   
106 -0.029195 -0.008360 -2.393736e-03      ...      -5.224276e-04   
107 -0.005053 -0.001062 -2.233502e-04      ...      -1.860154e-05   
108 -0.025394 -0.002825 -3.141824e-04      ...      -1.173600e-04   
109 -0.094153 -0.030415 -9.825095e-03      ...      -4.319068e-03   
110 -0.150614 -0.105205 -7.348635e-02      ...      -2.642423e-02   
111 -0.043583 -0.004956 -5.636009e-04      ...      -2.973716e-04   
112  0.215063 -0.080870  3.040931e-02      ...       2.095251e-02   
113  0.279764 -0.209153  1.563643e-01      ...       8.119893e-02   
114  0.174547 -0.145447  1.211992e-01      ...       4.274760e-02   
115  0.234520 -0.483743  9.978116e-01      ...       2.341782e-01   
116  0.000040 -0.006327  9.978116e-01      ...       4.006210e-05   
117 -0.012252  0.000593 -2.868634e-05      ...      -1.148159e-05   

               19            20            21            22            23  \
0    1.227829e-02  1.675424e-01  1.815630e-08  2.477505e-07  3.380660e-06   
1   -2.041205e-02  1.507518e-01  6.362953e-07 -4.699318e-06  3.470651e-05   
2   -4.907685e-02  1.589699e-01  9.526844e-05 -3.085938e-04  9.995978e-04   
3   -2.385083e-02  3.194040e-02  2.780914e-03 -3.724126e-03  4.987251e-03   
4   -2.412849e-02  2.189028e-02  1.827990e-02 -1.658422e-02  1.504584e-02   
5   -1.016696e-03  4.064690e-04  2.088401e-02 -8.349308e-03  3.338005e-03   
6   -5.546100e-07  4.787141e-08  3.977043e-03 -3.432803e-04  2.963039e-05   
7   -4.178462e-04 -2.626224e-04  8.190426e-04  5.147801e-04  3.235467e-04   
8    4.460706e-04 -1.079435e-02  2.173096e-11 -5.258619e-10  1.272520e-08   
9    9.199376e-03 -3.583858e-02  5.268249e-06 -2.052385e-05  7.995603e-05   
10   3.928766e-02 -5.760668e-02  3.275423e-03 -4.802685e-03  7.042077e-03   
11   3.906476e-02 -3.846113e-02  2.200925e-02 -2.166916e-02  2.133433e-02   
12   2.242867e-03 -8.546362e-04  6.796276e-02 -2.589696e-02  9.867941e-03   
13   8.618736e-04 -2.163487e-04  1.599267e-01 -4.014501e-02  1.007726e-02   
14   3.089336e-02  2.755401e-02  2.668725e-02  2.380255e-02  2.122966e-02   
15   3.709691e-02  6.712007e-02  1.114642e-03  2.016740e-03  3.648921e-03   
16   1.402339e-02  4.538330e-02  2.128217e-05  6.887460e-05  2.228960e-04   
17  -2.073971e-02  3.629394e-01  1.031840e-08 -1.805693e-07  3.159915e-06   
18  -4.152637e-02  1.675424e-01  2.717342e-05 -1.096340e-04  4.423297e-04   
19  -7.722912e-02  1.022488e-01  1.203168e-02 -1.592955e-02  2.109020e-02   
20  -7.701706e-02  7.597518e-02  4.923768e-02 -4.857159e-02  4.791451e-02   
21  -7.826643e-03  4.160278e-03  6.161208e-02 -3.275010e-02  1.740843e-02   
22  -4.071624e-10  3.508141e-12  4.387691e-02 -3.780467e-04  3.257278e-06   
23  -2.327289e-03 -1.506979e-03  5.574266e-03  3.609479e-03  2.337229e-03   
24  -2.874016e-03 -9.853026e-03  2.408803e-06  8.258129e-06  2.831144e-05   
25   2.633557e-02 -7.880889e-02  6.602294e-05 -1.975729e-04  5.912348e-04   
26   3.841235e-02 -4.416666e-02  1.024169e-02 -1.177593e-02  1.354000e-02   
27   5.550631e-02 -4.416666e-02  9.323951e-02 -7.419117e-02  5.903431e-02   
28  -1.234189e-02  4.854696e-02  7.156691e-06 -2.815092e-05  1.107320e-04   
29  -1.089371e-01  2.886619e-01  6.504056e-04 -1.723448e-03  4.566800e-03   
..            ...           ...           ...           ...           ...   
88  -1.008149e-01  1.764809e-01  4.335088e-03 -7.588765e-03  1.328447e-02   
89  -2.714243e-01  6.550082e-01  3.047249e-03 -7.353700e-03  1.774614e-02   
90  -3.392832e-01  6.045310e-01  1.708303e-02 -3.043835e-02  5.423472e-02   
91  -1.367692e-01  1.764809e-01  2.702587e-02 -3.487300e-02  4.499859e-02   
92   3.816035e-02  2.886619e-01  1.201724e-06  9.090376e-06  6.876365e-05   
93   4.065234e-02  6.550082e-01  3.439766e-08  5.542300e-07  8.929996e-06   
94  -1.009472e-01  9.604412e-01  1.284418e-06 -1.222033e-05  1.162677e-04   
95  -1.228112e-01  1.676725e+00  2.870847e-07 -3.919530e-06  5.351285e-05   
96   4.012965e-01  1.517566e+00  5.640054e-04  2.132876e-03  8.065808e-03   
97   1.828187e-01  3.794792e-01  3.908597e-03  8.113129e-03  1.684053e-02   
98   4.001259e-01  5.572149e-01  6.796276e-02  9.464487e-02  1.318023e-01   
99   1.590613e-01  1.281151e-01  3.111040e-01  2.505771e-01  2.018260e-01   
100  1.137406e-01  1.082832e-01  9.323951e-02  8.876577e-02  8.450669e-02   
101  1.077340e-04  1.007523e-05  1.508320e+00  1.410573e-01  1.319160e-02   
102 -5.268521e-03 -6.545481e-02  1.031840e-08  1.281933e-07  1.592643e-06   
103 -3.926524e-02 -1.058395e-01  1.760921e-04  4.746566e-04  1.279438e-03   
104 -2.748088e-03 -6.720616e-03  1.155103e-05  2.824873e-05  6.908398e-05   
105 -4.072252e-03 -2.507931e-03  1.387692e-02  8.546218e-03  5.263261e-03   
106 -1.495925e-04 -4.283449e-05  1.039560e-02  2.976689e-03  8.523486e-04   
107 -3.910778e-06 -8.221998e-07  5.776804e-04  1.214512e-04  2.553384e-05   
108 -1.305404e-05 -1.452010e-06  5.212125e-02  5.797485e-03  6.448585e-04   
109 -1.395219e-03 -4.507073e-04  8.494929e-02  2.744177e-02  8.864706e-03   
110 -1.845752e-02 -1.289272e-02  4.649294e-02  3.247567e-02  2.268450e-02   
111 -3.381630e-05 -3.845500e-06  1.468870e-01  1.670360e-02  1.899488e-03   
112 -7.878740e-03  2.962631e-03  3.271058e-01 -1.230011e-01  4.625193e-02   
113 -6.070482e-02  4.538330e-02  1.400354e-01 -1.046913e-01  7.826790e-02   
114 -3.562096e-02  2.968243e-02  4.387691e-02 -3.656200e-02  3.046659e-02   
115 -4.830370e-01  9.963553e-01  1.292688e-02 -2.666414e-02  5.499985e-02   
116 -6.317918e-03  9.963553e-01  6.472253e-14 -1.020695e-11  1.609667e-09   
117  5.555592e-07 -2.688181e-08  6.411816e-02 -3.102482e-03  1.501196e-04   

               24            25            26            27  
0    4.613055e-05  6.294709e-04  8.589398e-03  1.172060e-01  
1   -2.563226e-04  1.893054e-03 -1.398103e-02  1.032560e-01  
2   -3.237900e-03  1.048821e-02 -3.397345e-02  1.100469e-01  
3   -6.678793e-03  8.944062e-03 -1.197765e-02  1.604015e-02  
4   -1.365016e-02  1.238395e-02 -1.123519e-02  1.019299e-02  
5   -1.334515e-03  5.335313e-04 -2.133027e-04  8.527719e-05  
6   -2.557560e-06  2.207569e-07 -1.905473e-08  1.644718e-09  
7    2.033538e-04  1.278108e-04  8.033094e-05  5.048915e-05  
8   -3.079338e-07  7.451610e-06 -1.803196e-04  4.363507e-03  
9   -3.114897e-04  1.213490e-03 -4.727468e-03  1.841709e-02  
10  -1.032565e-02  1.514029e-02 -2.219989e-02  3.255123e-02  
11  -2.100467e-02  2.068010e-02 -2.036055e-02  2.004594e-02  
12  -3.760142e-03  1.432788e-03 -5.459587e-04  2.080355e-04  
13  -2.529607e-03  6.349853e-04 -1.593949e-04  4.001153e-05  
14   1.893489e-02  1.688817e-02  1.506268e-02  1.343451e-02  
15   6.602054e-03  1.194521e-02  2.161266e-02  3.910415e-02  
16   7.213493e-04  2.334473e-03  7.554959e-03  2.444980e-02  
17  -5.529766e-05  9.676943e-04 -1.693439e-02  2.963473e-01  
18  -1.784625e-03  7.200257e-03 -2.905018e-02  1.172060e-01  
19  -2.792274e-02  3.696881e-02 -4.894550e-02  6.480225e-02  
20  -4.726632e-02  4.662690e-02 -4.599613e-02  4.537389e-02  
21  -9.253507e-03  4.918732e-03 -2.614569e-03  1.389783e-03  
22  -2.806495e-08  2.418097e-10 -2.083450e-12  1.795116e-14  
23   1.513415e-03  9.799749e-04  6.345586e-04  4.108928e-04  
24   9.706047e-05  3.327536e-04  1.140783e-03  3.910962e-03  
25  -1.769264e-03  5.294502e-03 -1.584374e-02  4.741222e-02  
26  -1.556835e-02  1.790054e-02 -2.058211e-02  2.366538e-02  
27  -4.697391e-02  3.737739e-02 -2.974139e-02  2.366538e-02  
28  -4.355654e-04  1.713301e-03 -6.739289e-03  2.650907e-02  
29  -1.210113e-02  3.206562e-02 -8.496764e-02  2.251476e-01  
..            ...           ...           ...           ...  
88  -2.325507e-02  4.070904e-02 -7.126299e-02  1.247491e-01  
89  -4.282546e-02  1.033475e-01 -2.494009e-01  6.018608e-01  
90  -9.663481e-02  1.721828e-01 -3.067934e-01  5.466411e-01  
91  -5.806421e-02  7.492351e-02 -9.667801e-02  1.247491e-01  
92   5.201589e-04  3.934714e-03  2.976393e-02  2.251476e-01  
93   1.438840e-04  2.318322e-03  3.735381e-02  6.018608e-01  
94  -1.106205e-03  1.052476e-02 -1.001356e-01  9.527193e-01  
95  -7.306042e-04  9.974846e-03 -1.361853e-01  1.859321e+00  
96   3.050214e-02  1.153487e-01  4.362093e-01  1.649595e+00  
97   3.495612e-02  7.255891e-02  1.506115e-01  3.126264e-01  
98   1.835478e-01  2.556084e-01  3.559600e-01  4.957095e-01  
99   1.625597e-01  1.309329e-01  1.054592e-01  8.494161e-02  
100  8.045197e-02  7.659179e-02  7.291683e-02  6.941820e-02  
101  1.233672e-03  1.153723e-04  1.078956e-05  1.009034e-06  
102  1.978660e-05  2.458239e-04  3.054056e-03  3.794285e-02  
103  3.448726e-03  9.296047e-03  2.505751e-02  6.754255e-02  
104  1.689490e-04  4.131751e-04  1.010445e-03  2.471103e-03  
105  3.241424e-03  1.996259e-03  1.229413e-03  7.571443e-04  
106  2.440625e-04  6.988513e-05  2.001099e-05  5.729970e-06  
107  5.368219e-06  1.128611e-06  2.372786e-07  4.988533e-08  
108  7.172808e-05  7.978367e-06  8.874396e-07  9.871056e-08  
109  2.863628e-03  9.250579e-04  2.988279e-04  9.653248e-05  
110  1.584529e-02  1.106805e-02  7.731118e-03  5.400243e-03  
111  2.160048e-04  2.456349e-05  2.793294e-06  3.176460e-07  
112 -1.739205e-02  6.539906e-03 -2.459191e-03  9.247260e-04  
113 -5.851357e-02  4.374511e-02 -3.270412e-02  2.444980e-02  
114 -2.538737e-02  2.115493e-02 -1.762810e-02  1.468924e-02  
115 -1.134476e-01  2.340073e-01 -4.826843e-01  9.956280e-01  
116 -2.538495e-07  4.003286e-05 -6.313306e-03  9.956280e-01  
117 -7.263830e-06  3.514745e-07 -1.700678e-08  8.229060e-10  

[118 rows x 28 columns]

1.3 Função de custo e gradiente

Agora, você deverá implementar o código para calcular a função de custo e o gradiente para a regressão logística regularizada. Crie um arquivo de nome costFunctionReg.py que contém uma função de nome costFunctionReg.py e que computa o custo e o gradiente. Lembre-se de que a função de custo regularizada na regressão logística é dada por:

$$J_{regularizado} = \small \underbrace{-\frac{1}{m} \sum\limits_{i = 1}^{m} \large{(}\small y^{(i)}\log\left(x^{(i)}\right) + (1-y^{(i)})\log\left(1- x^{(i)}\right) \large{)} }_\text{Função de Custo} + \underbrace{\frac{\lambda}{2m} \sum\limits_{j = 1}^{n}\ {\theta}_{j}^{2} }_\text{Fator Regularização} $$

Depois de concluir a implementação da função costFunctionReg, você deve testar a corretude dela usando o valor inicial de ${\theta}$ (inicializado todo com zeros). Você deve ver que o custo é de cerca de 0.693. Porém, usando a função costFunctionReg, você agora deve computar os valores ótimos para ${\theta}$.


In [53]:
from numpy import loadtxt, where, zeros, e, array, log, ones, append, linspace
from pylab import scatter, show, legend, xlabel, ylabel, contour, title
from scipy.optimize import fmin_bfgs


def sigmoid(X):
    '''Compute the sigmoid function '''
    den = 1.0 + e ** (-1.0 * X)
    d = 1.0 / den
    return d


def cost_function_reg(theta, X, y, l):
    '''Compute the cost and partial derivatives as grads
    '''

    h = sigmoid(X.dot(theta))

    thetaR = theta[1:, 0]

    J = (1.0 / m) * ((-y.T.dot(log(h))) - ((1 - y.T).dot(log(1.0 - h)))) + (l / (2.0 * m)) * (thetaR.T.dot(thetaR))

    delta = h - y
    sumdelta = delta.T.dot(X[:, 1])
    grad1 = (1.0 / m) * sumdelta

    XR = X[:, 1:X.shape[1]]
    sumdelta = delta.T.dot(XR)

    grad = (1.0 / m) * (sumdelta + l * thetaR)

    out = zeros(shape=(grad.shape[0], grad.shape[1] + 1))

    out[:, 0] = grad1
    out[:, 1:] = grad

    return J.flatten(), out.T.flatten()

1.3.1 Testando a Função de Custo e o Gradiente


In [54]:
from scipy.optimize import fmin_bfgs

m, n = X.shape
y.shape = (m, 1)
it = map_feature(X[:, 0], X[:, 1])

#Initialize theta parameters
initial_theta = zeros(shape=(it.shape[1], 1))

#Set regularization parameter lambda to 1
regularizacao = 1

# Compute and display initial cost and gradient for regularized logistic regression
cost, grad = cost_function_reg(initial_theta, it, y, regularizacao)

#def decorated_cost(theta):
#    return cost_function_reg(theta, it, y, l)

#cost_function_reg(theta, it, y, l)

#print fmin_bfgs(decorated_cost, initial_theta, args=(it, y, l), maxfun=400)

cost, grad


Out[54]:
(array([ 0.69314718]),
 array([  1.87880932e-02,   1.87880932e-02,   7.77711864e-05,
          5.03446395e-02,   1.15013308e-02,   3.76648474e-02,
          1.83559872e-02,   7.32393391e-03,   8.19244468e-03,
          2.34764889e-02,   3.93486234e-02,   2.23923907e-03,
          1.28600503e-02,   3.09593720e-03,   3.93028171e-02,
          1.99707467e-02,   4.32983232e-03,   3.38643902e-03,
          5.83822078e-03,   4.47629067e-03,   3.10079849e-02,
          3.10312442e-02,   1.09740238e-03,   6.31570797e-03,
          4.08503006e-04,   7.26504316e-03,   1.37646175e-03,
          3.87936363e-02]))

1.4 Esboço da fronteira de decisão

Nessa parte, você deve esboçar (plotar) a fronteira de decisão que foi aprendida para separar os exemplos positivos dos negativos. Crie uma arquivo de nome plotDecisionBoundary.py, para criar esse gráfico que traça o limite da decisão não-linear. Seu gráfico deve ser semelhante ao apresentado na Figura abaixo.


In [60]:
#Plot Boundary
l = 1
m, n = X.shape
y.shape = (m, 1)
it = map_feature(X[:, 0], X[:, 1])
theta = ones(shape=(it.shape[1], 1))

u = linspace(-1, 1.5, 50)
v = linspace(-1, 1.5, 50)
z = zeros(shape=(len(u), len(v)))
for i in range(len(u)):
    for j in range(len(v)):
        z[i, j] = (map_feature(array(u[i]), array(v[j])).dot(array(theta)))

z = z.T
contour(u, v, z)
title('lambda = %f' % l)
xlabel('Microchip Test 1')
ylabel('Microchip Test 2')
legend(['y = 1', 'y = 0', 'Decision boundary'])
show()



In [61]:
#load the dataset
data2 = np.loadtxt('am-T2-dados/ex2data2.txt', delimiter=',')

In [82]:
#y = np.c_[data2[:,2]]
y = data2[:,2]
y.shape = (y.size, 1)
X = data2[:,0:2]

In [89]:
print(y.shape)


(118, 1)

In [84]:
#it = map_feature(X[:, 0], X[:, 1])

X_feature = map_feature(X[:, 0], X[:, 1])
X_feature.shape


Out[84]:
(118, 28)

In [85]:
def costFunctionReg(theta, reg, X, y):
    m = y.size
    h = sigmoid(X.dot(theta))
    J = -1*(1/m)*(np.log(h).T.dot(y)+np.log(1-h).T.dot(1-y)) + (reg/(2*m))*np.sum(np.square(theta[1:]))
    
    if np.isnan(J[0]):
        return(np.inf)
    return(J[0])

In [86]:
def gradientReg(theta, reg,  X, y):
    m = y.size
    h = sigmoid(X.dot(theta.reshape(-1,1)))
      
    grad = (1/m)*X.T.dot(h-y) + (reg/m)*np.r_[[[0]],theta[1:].reshape(-1,1)]
        
    return(grad.flatten())

In [87]:
initial_theta = np.zeros(X_feature.shape[1])
costFunctionReg(initial_theta, 1, X_feature, y)


Out[87]:
0.69314718055994529

In [100]:
initial_theta


Out[100]:
array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.])

1.3 Visualização de J(ø)

Para melhor entender a função de custo, você irá nessa parte do trabalho plotar o custo sobre uma grade bidimensional de valores de 0 e de 1. Para isso, você deve usar sua implementação da função computarCusto.

O código que você deve implementar deve gerar um array bidimensional de valores de J(ø). Os valores gerados pelo seu código devem estar na faixa a seguir: -10;0;+10 e -1;+4. Utilize incremento de 0.01 para gerar os valores de 0 e de 1.

A seguir, usando a função matplotlib.pyplot.contour da biblioteca matplotlib, produza um gráfico de curvas de contorno (contour plot). Também utilizando a biblioteca matplotlib, crie um gráfico da superfície correspondente a J(ø).


In [92]:
import pandas as pd
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt

from scipy.optimize import minimize

from sklearn.preprocessing import PolynomialFeatures

pd.set_option('display.notebook_repr_html', False)
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', 150)
pd.set_option('display.max_seq_items', None)
 
#%config InlineBackend.figure_formats = {'pdf',}
%matplotlib inline

import seaborn as sns
sns.set_context('notebook')
sns.set_style('white')

In [95]:
def predict(theta, X, threshold=0.5):
    p = sigmoid(X.dot(theta.T)) >= threshold
    return(p.astype('int'))

def loaddata(file, delimeter):
    data = np.loadtxt(file, delimiter=delimeter)
    print('Dimensions: ',data.shape)
    print(data[1:6,:])
    return(data)

def plotData(data, label_x, label_y, label_pos, label_neg, axes=None):
    # Get indexes for class 0 and class 1
    neg = data[:,2] == 0
    pos = data[:,2] == 1
    
    # If no specific axes object has been passed, get the current axes.
    if axes == None:
        axes = plt.gca()
    axes.scatter(data[pos][:,0], data[pos][:,1], marker='+', c='k', s=60, linewidth=2, label=label_pos)
    axes.scatter(data[neg][:,0], data[neg][:,1], c='y', s=60, label=label_neg)
    axes.set_xlabel(label_x)
    axes.set_ylabel(label_y)
    axes.legend(frameon= True, fancybox = True);

In [99]:
fig, axes = plt.subplots(1,3, sharey = True, figsize=(17,5))

# Decision boundaries
# Lambda = 0 : No regularization --> too flexible, overfitting the training data
# Lambda = 1 : Looks about right
# Lambda = 100 : Too much regularization --> high bias

for i, C in enumerate([0, 1, 100]):
    # Optimize costFunctionReg
    res2 = minimize(costFunctionReg, initial_theta, args=(C, X_feature, y), method=None, jac=gradientReg, options={'maxiter':3000})
    
    # Accuracy
    accuracy = 100*sum(predict(res2.x, X_feature) == y.ravel())/y.size    

    # Scatter plot of X,y
    plotData(data2, 'Microchip Test 1', 'Microchip Test 2', 'y = 1', 'y = 0', axes.flatten()[i])
    
    # Plot decisionboundary
    x1_min, x1_max = X[:,0].min(), X[:,0].max(),
    x2_min, x2_max = X[:,1].min(), X[:,1].max(),
    xx1, xx2 = np.meshgrid(np.linspace(x1_min, x1_max), np.linspace(x2_min, x2_max))
    
    h = sigmoid(X_feature.fit_transform(np.c_[xx1.ravel(), xx2.ravel()]).dot(res2.x))
    h = h.reshape(xx1.shape)
    axes.flatten()[i].contour(xx1, xx2, h, [0.5], linewidths=1, colors='g');       
    axes.flatten()[i].set_title('Train accuracy {}% with Lambda = {}'.format(np.round(accuracy, decimals=2), C))


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-99-04dbf6a05529> in <module>()
     23     h = sigmoid(X_feature).dot(res2.x)
     24     #h = h.reshape(xx1.shape)
---> 25     axes.flatten()[i].contour(xx1, xx2, h, [0.5], linewidths=1, colors='g');
     26     axes.flatten()[i].set_title('Train accuracy {}% with Lambda = {}'.format(np.round(accuracy, decimals=2), C))

/anaconda/lib/python3.6/site-packages/matplotlib/__init__.py in inner(ax, *args, **kwargs)
   1896                     warnings.warn(msg % (label_namer, func.__name__),
   1897                                   RuntimeWarning, stacklevel=2)
-> 1898             return func(ax, *args, **kwargs)
   1899         pre_doc = inner.__doc__
   1900         if pre_doc is None:

/anaconda/lib/python3.6/site-packages/matplotlib/axes/_axes.py in contour(self, *args, **kwargs)
   5823             self.cla()
   5824         kwargs['filled'] = False
-> 5825         contours = mcontour.QuadContourSet(self, *args, **kwargs)
   5826         self.autoscale_view()
   5827         return contours

/anaconda/lib/python3.6/site-packages/matplotlib/contour.py in __init__(self, ax, *args, **kwargs)
    862         self._transform = kwargs.get('transform', None)
    863 
--> 864         self._process_args(*args, **kwargs)
    865         self._process_levels()
    866 

/anaconda/lib/python3.6/site-packages/matplotlib/contour.py in _process_args(self, *args, **kwargs)
   1427                 self._corner_mask = mpl.rcParams['contour.corner_mask']
   1428 
-> 1429             x, y, z = self._contour_args(args, kwargs)
   1430 
   1431             _mask = ma.getmask(z)

/anaconda/lib/python3.6/site-packages/matplotlib/contour.py in _contour_args(self, args, kwargs)
   1506             args = args[1:]
   1507         elif Nargs <= 4:
-> 1508             x, y, z = self._check_xyz(args[:3], kwargs)
   1509             args = args[3:]
   1510         else:

/anaconda/lib/python3.6/site-packages/matplotlib/contour.py in _check_xyz(self, args, kwargs)
   1540 
   1541         if z.ndim != 2:
-> 1542             raise TypeError("Input z must be a 2D array.")
   1543         else:
   1544             Ny, Nx = z.shape

TypeError: Input z must be a 2D array.

2. Regressão Linear com Múltiplas Variáveis

Nessa parte do trabalho, você irá implementar a regressão linear com múltiplas variáveis para predizer o preço de venda de imóveis. O arquivo ex1data2.txt contém informações acerca de preços de imóveis. A primeira coluna corersponde ao tamanho do imóvel (em pés quadrados). A segunda coluna corresponde à quantidade de dormitórios no imóvel em questão. A terceira coluna corresponde ao preço do imóvel.

Visualização dos dados


In [ ]:
# Carregando o dataset na forma de txt, porque tem melhor desempenho p/ representação gráfica e 
# principalmente na vertorização, se comparado ao Dataframe, utilizado na primeira seção deste trabalho.

data = np.loadtxt('am-T1-dados/ex1data2.txt', delimiter=',')

In [ ]:
data

In [ ]:
# Inicializando visualização gráfica
plano = plt.figure()
plano = plano.add_subplot(111, projection='3d')

# Criação dos pontos ('x' vermelhos) para o gráfico
for c, m in [('red', 'x')]:
    tamanho = data[:, 0] 
    quartos = data[:, 1] 
    valor   = data[:, 2] 
    plano.scatter(tamanho, quartos, valor, c=c, marker=m)


# Plotando o Gráfico
plano.set_xlabel('Tamanho')
plano.set_ylabel('Nº de Quartos')
plano.set_zlabel('Valor do Imóvel')
show()

2.1 Normalização das características

Se você inspecionar os valores do conjunto de dados fornecido, irá notar que os tamanhos dos imóveis são aproximadamente 1000 vezes maiores que as quantidades encontradas na coluna de quantidade de dormitórios. Sua tarefa nessa parte é implementar uma função denominada normalizarCaracteristica em um arquivo denominado normalizarCaracteristica.py. Essa função deve:

  • subtrair o valor médio de todas as características do conjunto de dados.
  • após subtrair a média, dividir cada característica pelos seus respectivos desvios padrões.

A sua função normalizarCaracteristica deve a matriz de dados X de dados como parâmetro (na forma de um numpy array). Além disso, essa função deve funcionar com conjuntos de dados de variados tamanhos (qualquer quantidade de características / exemplos). Repare que cada coluna da matriz de dados X passada para a função normalizarCaracteristica deve corresponder a um característica.

Nota de Implementação: Ao normalizar as características, é importante armazenar os valores utilizados para a normalização - o valor médio e o desvio padrão utilizados para a normalização. Depois de aprender os parâmetros do modelo, muitas vezes queremos prever os preços das casas que não temos visto antes. Dado um novo valor x (área da sala de estar e número de quartos), devemos normalizar x usando a média e o desvio padrão que nós previamente calculamos a partir do conjunto de treinamento.


In [ ]:
def normalizarCaracteristica(X):
    '''
    Retorna uma versão normalizada de X onde o valor médio de cada característica é 0 
    e o desvio padrão é 1. Este é frequentemente um bom passo de pré-processamento 
    a ser feito ao trabalhar com algoritmos de aprendizado.
    '''
    valormedio = []
    desviopadrao = []
    X_Normalizado = X

    for i in range(X.shape[1]):
        m = np.mean(X[:, i])
        s = np.std(X[:, i])
        valormedio.append(m)
        desviopadrao.append(s)
        X_Normalizado[:, i] = (X_Normalizado[:, i] - m) / s

    return X_Normalizado, valormedio, desviopadrao

2.2 Gradiente descendente

Anteriormente, você implementou o GD em uma regressão linear univariada. A única diferença agora é que há mais uma característica na matriz de dados X. A função de hipótese h(x) e a atualização dos gradientes em lote permanecem inalteradas. Você deve implementar código nos arquivos denominados computarCustoMulti.py e gdmulti.py para implementar a função de custo e o algoritmo GD para regressão linear com múltiplas variáveis, respectivamente. Se o seu código na parte anterior (variável única) já provê suporte a múltiplas variáveis, você também pode reusá-lo aqui. Se assegure de que o seu código dá suporte a qualquer número de características e está bem vetorizado.


In [ ]:
def computarCustoMulti(X, y, theta):
    '''
    Função que computa o custo para Regressão Linear com múltiplas variáveis.
    '''
    # Número do conjunto de treinamento
    m = y.size
    J = (1 / (2 * m)) * (X.dot(theta) - y).T.dot(X.dot(theta) - y)
    return J

In [ ]:
def gradienteDescendenteMulti(X, y, theta, alpha, iteracoes):
    '''
    Essa função calcula o gradiente descendente conforme o Theta, e com
    etapas de iteracoes gradiente mediante a taxa de aprendizado em Alpha.
    '''
    m = y.size
    J = np.zeros(shape=(iteracoes, 1))

    for i in range(iteracoes):
        hipotese = X.dot(theta)
        
        for it in range(theta.size):
            temp = X[:, it]
            temp.shape = (m, 1)

            errors_x1 = (hipotese - y) * temp
            theta[it][0] = theta[it][0] - alpha * (1.0 / m) * errors_x1.sum()

        J[i, 0] = computarCustoMulti(X, y, theta)

    return J

In [ ]:
X = data[:, :2]
y = data[:, 2]

# Tamanho do conjunto de treinamento
m = y.size
y.shape = (m,1)

# Normalizando X, obtendo Média e Desvio-padrão
x, media, desviopadrao = normalizarCaracteristica(X)

# Adicionando uma coluna de 1's ao novo X
Xnovo = np.ones(shape=(m, 3))
Xnovo[:, 1:3] = x

# Atributos para a função GradienteDescendenteMulti
iteracao = 100           # Número de repeticões p/ o algoritmo
alpha = 0.01             # Taxa de aprendizado

# Inicializando o Theta p/ execução da função GradienteDescendenteMulti
theta = np.zeros(shape=(3, 1))
J = gradienteDescendenteMulti(Xnovo, y, theta, alpha, iteracao)

plot(np.arange(iteracao), J)
xlabel('Iteracões')
ylabel('Função de Custo')
show()

3. Regressão Logística

Nessa parte do trabalho, você irá implementar a regressão logística. Em particular, você irá criar uma classificador para predizer se um estudante será admitido em uma universidade, com base nos resultados de duas avaliações. Suponha que estão disponíveis dados históricos acerca de realizações passadas dessas avaliações, e que esses dados históricos podem ser usados como conjunto de treinamento. Para cada exemplo desse conjunto de treinamento, temos as notas das duas avaliações e a decisão acerca do candidato (aprovado ou reprovado).

Sua tarefa é construir um modelo de classificação que provê uma estimativa da probabilidade de admissão de um candidato, com base na notas que ele obteve nas duas avaliações.

O arquivo ex2data1.txt contém os dados a serem usados nessa parte do trabalho.

3.1 Visualização dos dados

Antes de começar a implementar qualquer algoritmo de aprendizado, é adequado visualizar os dados, quando possível. Nessa parte do trabalho, você deve carregar o arquivo com o conjunto de treinamento e plotar (i.e., produzir um gráfico) os pontos de dados. O resultado dessa tarefa deve ser um gráfico similar ao apresentado na Figura abaixo.


In [ ]:
# carregando os dados 
data = np.loadtxt('am-T1-dados/ex2data1.txt', delimiter=',', usecols=(0,1,2), unpack=True)

In [ ]:
# Transportando matriz
X = np.transpose(np.array(data[:2]))
y = np.transpose(np.array(data[2:]))

# Tamanho do conjunto de treinamento
m = y.size

# Adicionando uma coluna de 1's ao novo X
X = np.insert(X,0,1,axis=1)

# Classificando a amostra em Positiva (data[:, 2]=1) e Negativa(data[:, 2]=0)
X_Admitted  = np.array([X[i] for i in range(X.shape[0]) if y[i] == 1])
X_Nadmitted = np.array([X[i] for i in range(X.shape[0]) if y[i] == 0])

plt.figure(figsize=(16,8))
plt.plot(X_Admitted[:, 1], X_Admitted[:, 2],'k+',label='Admitted')
plt.plot(X_Nadmitted[:, 1], X_Nadmitted[:, 2],'yo',label='Not admitted')
plt.title('Gráfico Decision Boundary p/ admissão de candidato', fontsize=18, fontweight='bold')
plt.xlabel('Exam 1 Score', fontweight='bold')
plt.ylabel('Exam 2 Score', fontweight='bold')
plt.legend()
plt.grid(False)    
plt.show()

3.2 Implementação

3.2.1 Função sigmoide

Como primeiro passo nessa parte, implemente a função em Python que calcula o valor da função sigmoide. Defina essa função em um arquivo denominado sigmoide.py, de tal forma que ela possa ser chamada de outras parte do seu código. Após finalizar sua implementação, você pode verificar sua corretude:

  • Para a sigmoide(0), o valor retornado deve ser 0.5.
  • Para valores muito grandes positivos (ou negativos), ela retornará um valor muito próximo de 1 (ou de 0).

O seu código também deve funcionar com vetores (i.e., o seu código deve estar vetorizado). Em particular, se uma matriz for passada, o seu código deve aplicar a função sigmoide a cada componente.


In [ ]:
def sigmoid(x):
    '''
    A função sigmoid
    '''
    g = np.array([x]).flatten()
    s = 1 / (1 + np.exp(-g))
    return s

In [ ]:
print('\t ###########################  Teste para função sigmoid(0)  ###########################\n')
print('\t O Valor da Sigmoid(0) é', sigmoid(0))
print('\t O Valor da Sigmoid([0,1,2,3000]) é', sigmoid(np.array([0,1,2,3000])))
print('\t ######################################################################################\n')

In [ ]:
# Exibindo o gráfico da função Sigmoid
X_teste = np.arange(-6,6,.5)
plt.plot(X_teste, sigmoid(X_teste))
plt.title("Função Sigmoid", fontsize=18, fontweight='bold')
plt.grid(True)
plt.show()

3.2.2 Função de custo e gradiente

Agora, você deverá implementar a função de custo para a regressão logística. Essa função deve retornar o valor de função de custo e o gradiente. Implemente esse código em um arquivo denominado funcaoCustoRegressaoLogistica.py. Lembre-se de que o gradiente é um vetor com o mesmo número de elementos que ø.

Uma vez que tenha implementado essa função, realize uma chamada usando o valor inicial de ø. Você deve confirmar que o valor produzido é aproximadamente 0.693.


In [ ]:
def custoJ(theta, X, y):
    '''
    A função custoJ retorna o valor de função de custo:
        X é uma matrix com n-colunas e m-linhas
        y é um vetor com m-linhas
        theta é um vetor n-dimensional
        
    Obs.: Será utilizada para facilitar o cálculo de minimização. 
    '''
    m = len(y)
    H = sigmoid(X.dot(theta).T)
    J = -np.sum( y* np.log(H) + (1-y) * np.log(1-H))/m
    return J

In [ ]:
def custoRegressaoLogistica(theta, X, y):
    '''
    A função funcaoCustoRegressaoLogistica retorna o valor de função de custo e o gradiente.
        Returna J, gradiente:
        
        X é uma matrix com n-colunas e m-linhas
        y é um vetor com m-linhas
        theta é um vetor n-dimensional
    '''
    
    # Calcula o Custo
    m = len(y)
    H = sigmoid(X.dot(theta).T)
    J = -np.sum( y* np.log(H) + (1-y) * np.log(1-H))/m

    # Calcula o Gradiente
    erro = H-y
    gradiente = []
    for i in range(len(X.columns)):
        gradiente.append(np.sum(erro*(X.iloc[:,i]))/m)
    
    return J, gradiente

3.2.3 Aprendizado dos parâmetros

Para a regressão logística, o objetivo é minimizar J(ø) com relação ao vetor de parâmetros ø. Sendo assim, nessa parte você deve implementar uma função em Python para encontrar o vetor ø que minimiza a função de custo. Utilize a função funcaoCustoRegressaoLogistica que você implementou previamente.


In [ ]:
from scipy import optimize

def minimizar(theta, X, y):
    '''
    A função minimizar J(ø) com relação ao vetor de parâmetros ø
    '''
    minimo = optimize.fmin(func=custoJ, x0=theta, args=(X, y), maxiter=1000, full_output=True)
    return minimo[0], minimo[1]

In [12]:
# Utilizando uma segunda estrutura de dados
import pandas as pd

dfQA = pd.read_csv('am-T2-dados/ex2data2.txt', names=['Exame1', 'Exame2', 'Admissao'])

X = dfQA.iloc[:, :2]
y = dfQA.iloc[:, 2]
X.head()


Out[12]:
Exame1 Exame2
0 0.051267 0.69956
1 -0.092742 0.68494
2 -0.213710 0.69225
3 -0.375000 0.50219
4 -0.513250 0.46564

In [13]:
#add a column of ones to the feature matrix X to account for theta 0
m = len(y)
X.insert(0, "theta0",value=pd.Series(np.ones([m])))
X.head()


Out[13]:
theta0 Exame1 Exame2
0 1.0 0.051267 0.69956
1 1.0 -0.092742 0.68494
2 1.0 -0.213710 0.69225
3 1.0 -0.375000 0.50219
4 1.0 -0.513250 0.46564

In [18]:
# Apresentando uma visualização gráfica com base na classificação
from seaborn import lmplot
import matplotlib.pyplot as plt

g = lmplot("Exame1", "Exame2", hue="Admissao", data=dfQA, fit_reg=True, palette = "dark", markers = ["o","x"], legend = True)
plt.xlabel("Exame 1 Score")
plt.ylabel("Exame 2 Score")
plot_x = np.array([min(X.iloc[:,2])-2,  max(X.iloc[:,2])+2])
plt.ylim(30,100)
plt.show()



In [19]:
theta0 = np.zeros([X.shape[1], 1])
hypothesis = sigmoid(X.dot(theta0).T)
print(hypothesis)


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-19-c0e412851592> in <module>()
      1 theta0 = np.zeros([X.shape[1], 1])
----> 2 hypothesis = sigmoid(X.dot(theta0).T)
      3 print(hypothesis)

NameError: name 'sigmoid' is not defined

In [ ]:
print(custoRegressaoLogistica(theta0, X, y))

In [ ]:
# 
print(custoJ(theta0, X, y))

In [ ]:
# Avaliando a minimização
X_min, theta_min = minimizar(theta0, X, y)
print(X_min, theta_min)

3.2.4 Avaliação do modelo

Após o aprendizado dos parâmetros, você pode usar o modelo correspondente para predizer se um candidato qualquer será aprovado. Para um candidato com notas 45 e 85 na primeira e segunda avaliações, respectivamente, você deve esperar que ele seja aprovado com probabilidade de 77.6%.

Outro modo de avaliar a qualidade dos parâmetros é verificar o quão bem o modelo aprendido prediz os pontos de dados do conjunto de treinamento. Nessa parte, você deve implementar uma função denominada predizer. Essa função deve produzir os valores 0 ou 1, dados um exemplo do conjunto de treinamento o vetor de parâmetros ø. Use essa função para produzir a porcentagem de acertos do seu classificador sobre o conjunto de treinamento.


In [ ]:
def predizer(theta, X):
    P = sigmoid(X.dot(theta))
    return (P >= 0.5).astype(int)

In [ ]:
# Predição de Admissão do candidato
A = np.array([1,45,85])
H = sigmoid(A.dot(X_min))

# Efetuando predição p/ Admissão de um candidato com notas 45 e 85 na primeira e segunda avaliações 
print('\t ######################################  ALUNO TESTE  #################################\n')
print('\t Para as notas 45 e 85 no 1º e 2º Exame prevê a probabilidade de admissão de %f' % H)
print('\t ######################################################################################\n')

2 - L2 Regularization

The standard way to avoid overfitting is called L2 regularization. It consists of appropriately modifying your cost function, from: $$J = -\frac{1}{m} \sum\limits_{i = 1}^{m} \large{(}\small y^{(i)}\log\left(a^{[L](i)}\right) + (1-y^{(i)})\log\left(1- a^{[L](i)}\right) \large{)} \tag{1}$$ To: $$J_{regularized} = \small \underbrace{-\frac{1}{m} \sum\limits_{i = 1}^{m} \large{(}\small y^{(i)}\log\left(a^{[L](i)}\right) + (1-y^{(i)})\log\left(1- a^{[L](i)}\right) \large{)} }_\text{cross-entropy cost} + \underbrace{\frac{1}{m} \frac{\lambda}{2} \sum\limits_l\sum\limits_k\sum\limits_j W_{k,j}^{[l]2} }_\text{L2 regularization cost} \tag{2}$$

Let's modify your cost and observe the consequences.

Exercise: Implement compute_cost_with_regularization() which computes the cost given by formula (2). To calculate $\sum\limits_k\sum\limits_j W_{k,j}^{[l]2}$ , use :

np.sum(np.square(Wl))

Note that you have to do this for $W^{[1]}$, $W^{[2]}$ and $W^{[3]}$, then sum the three terms and multiply by $ \frac{1}{m} \frac{\lambda}{2} $.


In [ ]:


In [6]:
import numpy as np

# função de custo
def loss_function(theta, X, y, Lambda):
    
    m = y.size
    H = np.subtract(X,y)
    J = 1/(2*m) * np.sum(H**2)
    custo = J + (1/(2*m) * np.multiply(Lambda,np.sum(theta**2)))
    return custo

def prints(self, epoch):
    print("--epoca %s: " % epoch)
    print("loss: ", self.loss[epoch])
    print("theta: ", self.theta_0.reshape(theta[0].shape[0]), self.theta_n.reshape(theta[1].shape[0]))

def gradient_descent(self, epochs, X, Y, learning_rate, Lambda, m, print_results):
    for i in xrange(epochs):
        # calcula H
        H = np.dot(self.theta_n.T, X) + self.theta_0

        # calcula gradientes
        gH = H - Y

        gTheta_n = np.dot(X, gH.T)/m
        gTheta_0 = np.sum(gH)/m

        # calcula função de custo
        loss = self.loss_function(Y, gH, Lambda, m)
        self.loss.append(loss)

        # atualiza pesos
        self.theta_0 -= learning_rate*gTheta_0
        self.theta_n = self.theta_n*(1-(learning_rate*Lambda/m)) - learning_rate*gTheta_n

        if print_results:
            self.prints(i)

    # calcula função de custo final
    # calcula H
    H = np.dot(self.theta_n.T, X) + self.theta_0

    # calcula gradientes
    gH = H - Y
    loss = self.loss_function(Y, gH, Lambda, m)
    self.loss.append(loss)

In [ ]: